#pragma once

#include <AST/ExprAST.hpp>
#include <string>
#include <cassert>

class Var : public Expr {
  protected:
    using AllocaInst = llvm::AllocaInst;

  protected:
    // 变量名
    string name;

    // 是否正在定义
    bool isDefinition = false;

    // 是否常量
    bool isConst = true;

    // 是否可以改为null
    bool isNullable = false;

    // 是否需要初始化
    Expr *initExpr = nullptr;

    // 声明类型；如果有initExpr，则可以自动推导
    Type *type = nullptr;

  public:
    // 引用一个已经定义的变量
    Var(string name);

    // 定义一个新的变量
    Var(string name,
        bool isDefinition,
        bool isConst = true,
        bool isNullable = false,
        Expr *initExpr = nullptr,
        Type *type = nullptr);

    // 根据形参生成一个模板变量
    Var(const llvm::Argument *arg);

    virtual Value *codegen() override;

    virtual Value *refCodegen();

    const string &getName() const;

    virtual AllocaInst *createEntryAlloca(Function *fun);

    virtual AllocaInst *createAlloca();

    inline Type *getType() const {
        assert(type != nullptr);
        return type;
    }

    friend class graphviz::GraphGenerator;

    virtual VisitResult accept(ASTVisitor &visitor) const override {
        return visitor.visitVarAST(this);
    }

  protected:
    virtual Value *valCodegen();

    virtual Value *defCodegen();

    virtual Type *updateTypeByVal(const Value *val);

    virtual Type *updateTypeByPtr(const Value *ptr);
};